package org.osflash.signals.natives;

import asunit.asserts.*;
import asunit.framework.IAsync;
import org.osflash.signals.IPrioritySignal;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;

class NativeMappedSignalFunctionArgTest {

	@:meta(Inject())
	public var async : IAsync;
	var signal : NativeMappedSignal;
	var signalMappingToEventType : NativeMappedSignal;
	var signalMappingToIncorrectEventType : NativeMappedSignal;
	var signalMappingToVoid : NativeMappedSignal;
	var sprite : Sprite;
	var EventType : String;
	var MappedObject : String;
	@:meta(Before())
	public function setUp() : Void {
		sprite = new Sprite();
		signal = new NativeMappedSignal(sprite, EventType, MouseEvent, String).mapTo(function() : String {
			return MappedObject;
		}
);
		signalMappingToEventType = new NativeMappedSignal(sprite, EventType, MouseEvent, String).mapTo(function(event : MouseEvent) : String {
			return event.type;
		}
);
		signalMappingToIncorrectEventType = new NativeMappedSignal(sprite, EventType, MouseEvent, String).mapTo(function(event : MouseEvent) : Int {
			return event.delta;
		}
);
		signalMappingToVoid = new NativeMappedSignal(sprite, EventType).mapTo(function() : Void {
		}
);
	}

	@:meta(After())
	public function tearDown() : Void {
		signal.removeAll();
		signal = null;
		signalMappingToEventType.removeAll();
		signalMappingToEventType = null;
		signalMappingToIncorrectEventType.removeAll();
		signalMappingToIncorrectEventType = null;
		signalMappingToVoid.removeAll();
		signalMappingToVoid = null;
	}

	@:meta(Test())
	public function testInstantiated() : Void {
		assertFalse("sprite has no click event listener to start", sprite.hasEventListener(EventType));
		assertTrue("NativeMappedSignal instantiated", Std.is(signal, NativeMappedSignal));
		assertTrue("implements IPrioritySignal", Std.is(signal, IPrioritySignal));
		assertSame("has only one value class", 1, signal.valueClasses.length);
		assertSame("single value class is of type String", String, signal.valueClasses[0]);
		assertTrue("NativeMappedSignal instantiated", Std.is(signalMappingToEventType, NativeMappedSignal));
		assertTrue("implements IPrioritySignal", Std.is(signalMappingToEventType, IPrioritySignal));
		assertSame("has only one value class", 1, signalMappingToEventType.valueClasses.length);
		assertSame("single value class is of type String", String, signalMappingToEventType.valueClasses[0]);
		assertTrue("NativeMappedSignal instantiated", Std.is(signalMappingToIncorrectEventType, NativeMappedSignal));
		assertTrue("implements IPrioritySignal", Std.is(signalMappingToIncorrectEventType, IPrioritySignal));
		assertSame("has only one value class", 1, signalMappingToIncorrectEventType.valueClasses.length);
		assertSame("single value class is of type String", String, signalMappingToIncorrectEventType.valueClasses[0]);
		assertTrue("NativeMappedSignal instantiated", Std.is(signalMappingToVoid, NativeMappedSignal));
		assertTrue("implements IPrioritySignal", Std.is(signalMappingToVoid, IPrioritySignal));
		assertSame("has no value classes", 0, signalMappingToVoid.valueClasses.length);
	}

	function dispatchTestEvent() : Void {
		sprite.dispatchEvent(buildTestEvent());
	}

	function buildTestEvent() : Event {
		return new MouseEvent(EventType);
	}

	//////
	@:meta(Test())
	public function signal_add_then_mapped_object_should_be_callback_argument() : Void {
		signal.add(async.add(checkMappedArgument, 10));
		dispatchTestEvent();
	}

	function checkMappedArgument(argument : String) : Void {
		assertSame(MappedObject, argument);
	}

	//////
	@:meta(Test())
	public function mapping_function_should_receive_event_as_argument() : Void {
		signalMappingToEventType.add(async.add(checkMappedEventTypeArgument, 10));
		dispatchTestEvent();
	}

	function checkMappedEventTypeArgument(argument : String) : Void {
		assertSame(EventType, argument);
	}

	@:meta(Test(expects="ArgumentError"))
	public function mapping_function_has_to_many_arguments_should_throw_ArgumentError() : Void {
		new NativeMappedSignal(sprite, EventType, MouseEvent, String).mapTo(function(event : MouseEvent, extraArg : Dynamic) : String {
			return event.type;
		}
);
	}

	@:meta(Test(expects="Error"))
	public function mapping_function_returns_incorrectly_typed_argument_should_throw_Error() : Void {
		signalMappingToIncorrectEventType.dispatch(buildTestEvent());
	}

	function emptyHandler(argument : String) : Void {
	}

	@:meta(Test())
	public function mapping_to_void() : Void {
		signalMappingToVoid.add(async.add(emptyHandler, 10));
		dispatchTestEvent();
	}

}

